"""
部门服务层
负责部门管理的业务逻辑
"""
from typing import List, Optional, Dict, Any
from datetime import datetime
from motor.motor_asyncio import AsyncIOMotorDatabase  # pyright: ignore[reportMissingImports]
from bson import ObjectId  # pyright: ignore[reportMissingImports]
from app.schemas.department import DepartmentCreate, DepartmentUpdate, DepartmentResponse
import logging

logger = logging.getLogger(__name__)


class DepartmentService:
    """部门服务类"""
    
    @staticmethod
    async def get_departments(
        db: AsyncIOMotorDatabase,
        tree: bool = False,
        include_users: bool = False
    ) -> List[Dict[str, Any]]:
        """
        获取部门列表
        
        Args:
            db: 数据库连接
            tree: 是否返回树形结构
            include_users: 是否包含用户数量
            
        Returns:
            部门列表
        """
        try:
            # 查询所有部门
            cursor = db.departments.find({}).sort([("order", 1), ("created_at", 1)])
            departments = await cursor.to_list(length=None)
            
            # 转换为字典格式
            department_list = []
            department_map = {}
            
            for dept in departments:
                dept_id = str(dept["_id"])
                dept_dict = {
                    "id": dept_id,
                    "name": dept.get("name", ""),
                    "parent_id": str(dept["parent_id"]) if dept.get("parent_id") else None,
                    "manager_id": str(dept["manager_id"]) if dept.get("manager_id") else None,
                    "manager_name": None,
                    "order": dept.get("order", 0),
                    "description": dept.get("description", ""),
                    "children": [],
                    "user_count": 0,
                    "created_at": dept.get("created_at"),
                    "updated_at": dept.get("updated_at")
                }
                
                # 获取部门负责人信息
                if dept_dict["manager_id"]:
                    try:
                        manager = await db.users.find_one({"_id": ObjectId(dept_dict["manager_id"])})
                        if manager:
                            dept_dict["manager_name"] = manager.get("name", "")
                    except Exception as e:
                        logger.warning(f"获取部门负责人信息失败: {e}")
                
                # 获取部门用户数量
                if include_users:
                    user_count = await db.users.count_documents({
                        "department_id": dept_id,
                        "status": {"$ne": "deleted"}
                    })
                    dept_dict["user_count"] = user_count
                
                department_list.append(dept_dict)
                department_map[dept_id] = dept_dict
            
            if tree:
                # 构建树形结构
                root_departments = []
                for dept in department_list:
                    if dept["parent_id"] and dept["parent_id"] in department_map:
                        # 有父部门，添加到父部门的children中
                        parent = department_map[dept["parent_id"]]
                        parent["children"].append(dept)
                    else:
                        # 根部门
                        root_departments.append(dept)
                
                return root_departments
            else:
                return department_list
                
        except Exception as e:
            logger.error(f"获取部门列表失败: {e}")
            raise
    
    @staticmethod
    async def get_department(
        db: AsyncIOMotorDatabase,
        department_id: str
    ) -> Optional[Dict[str, Any]]:
        """
        获取部门详情
        
        Args:
            db: 数据库连接
            department_id: 部门ID
            
        Returns:
            部门信息，如果不存在返回None
        """
        try:
            if not ObjectId.is_valid(department_id):
                return None
            
            dept = await db.departments.find_one({"_id": ObjectId(department_id)})
            if not dept:
                return None
            
            dept_dict = {
                "id": str(dept["_id"]),
                "name": dept.get("name", ""),
                "parent_id": str(dept["parent_id"]) if dept.get("parent_id") else None,
                "manager_id": str(dept["manager_id"]) if dept.get("manager_id") else None,
                "manager_name": None,
                "order": dept.get("order", 0),
                "description": dept.get("description", ""),
                "children": [],
                "user_count": 0,
                "created_at": dept.get("created_at"),
                "updated_at": dept.get("updated_at")
            }
            
            # 获取部门负责人信息
            if dept_dict["manager_id"]:
                try:
                    manager = await db.users.find_one({"_id": ObjectId(dept_dict["manager_id"])})
                    if manager:
                        dept_dict["manager_name"] = manager.get("name", "")
                except Exception as e:
                    logger.warning(f"获取部门负责人信息失败: {e}")
            
            # 获取部门用户数量
            user_count = await db.users.count_documents({
                "department_id": department_id,
                "status": {"$ne": "deleted"}
            })
            dept_dict["user_count"] = user_count
            
            return dept_dict
            
        except Exception as e:
            logger.error(f"获取部门详情失败: {e}")
            raise
    
    @staticmethod
    async def create_department(
        db: AsyncIOMotorDatabase,
        department_in: DepartmentCreate
    ) -> Dict[str, Any]:
        """
        创建部门
        
        Args:
            db: 数据库连接
            department_in: 部门创建数据
            
        Returns:
            创建的部门信息
        """
        try:
            # 检查部门名称是否已存在
            existing = await db.departments.find_one({"name": department_in.name})
            if existing:
                raise ValueError(f"部门名称 '{department_in.name}' 已存在")
            
            # 验证父部门是否存在
            if department_in.parent_id:
                if not ObjectId.is_valid(department_in.parent_id):
                    raise ValueError("无效的父部门ID")
                parent = await db.departments.find_one({"_id": ObjectId(department_in.parent_id)})
                if not parent:
                    raise ValueError("父部门不存在")
            
            # 验证部门负责人是否存在
            if department_in.manager_id:
                if not ObjectId.is_valid(department_in.manager_id):
                    raise ValueError("无效的部门负责人ID")
                manager = await db.users.find_one({"_id": ObjectId(department_in.manager_id)})
                if not manager:
                    raise ValueError("部门负责人不存在")
            
            # 创建部门文档
            dept_doc = department_in.model_dump()
            if dept_doc.get("parent_id"):
                dept_doc["parent_id"] = ObjectId(dept_doc["parent_id"])
            if dept_doc.get("manager_id"):
                dept_doc["manager_id"] = ObjectId(dept_doc["manager_id"])
            
            dept_doc.update({
                "created_at": datetime.utcnow(),
                "updated_at": datetime.utcnow()
            })
            
            result = await db.departments.insert_one(dept_doc)
            dept_id = str(result.inserted_id)
            
            # 返回创建的部门
            return await DepartmentService.get_department(db, dept_id)
            
        except ValueError:
            raise
        except Exception as e:
            logger.error(f"创建部门失败: {e}")
            raise ValueError(f"创建部门失败: {str(e)}")
    
    @staticmethod
    async def update_department(
        db: AsyncIOMotorDatabase,
        department_id: str,
        department_in: DepartmentUpdate
    ) -> Dict[str, Any]:
        """
        更新部门
        
        Args:
            db: 数据库连接
            department_id: 部门ID
            department_in: 部门更新数据
            
        Returns:
            更新后的部门信息
        """
        try:
            if not ObjectId.is_valid(department_id):
                raise ValueError("无效的部门ID")
            
            # 检查部门是否存在
            existing = await db.departments.find_one({"_id": ObjectId(department_id)})
            if not existing:
                raise ValueError("部门不存在")
            
            # 如果更新名称，检查是否与其他部门重名
            if department_in.name and department_in.name != existing.get("name"):
                name_exists = await db.departments.find_one({
                    "name": department_in.name,
                    "_id": {"$ne": ObjectId(department_id)}
                })
                if name_exists:
                    raise ValueError(f"部门名称 '{department_in.name}' 已存在")
            
            # 验证父部门（不能将自己设为父部门）
            if department_in.parent_id:
                if department_in.parent_id == department_id:
                    raise ValueError("不能将自己设为父部门")
                if not ObjectId.is_valid(department_in.parent_id):
                    raise ValueError("无效的父部门ID")
                parent = await db.departments.find_one({"_id": ObjectId(department_in.parent_id)})
                if not parent:
                    raise ValueError("父部门不存在")
                
                # 检查是否会造成循环引用
                current_parent_id = department_in.parent_id
                visited = {department_id}
                while current_parent_id:
                    if current_parent_id in visited:
                        raise ValueError("不能创建循环的部门层级关系")
                    visited.add(current_parent_id)
                    parent_dept = await db.departments.find_one({"_id": ObjectId(current_parent_id)})
                    if not parent_dept or not parent_dept.get("parent_id"):
                        break
                    current_parent_id = str(parent_dept["parent_id"])
            
            # 验证部门负责人
            if department_in.manager_id:
                if not ObjectId.is_valid(department_in.manager_id):
                    raise ValueError("无效的部门负责人ID")
                manager = await db.users.find_one({"_id": ObjectId(department_in.manager_id)})
                if not manager:
                    raise ValueError("部门负责人不存在")
            
            # 构建更新数据
            update_data = department_in.model_dump(exclude_unset=True)
            if update_data.get("parent_id"):
                update_data["parent_id"] = ObjectId(update_data["parent_id"])
            if update_data.get("manager_id"):
                update_data["manager_id"] = ObjectId(update_data["manager_id"])
            
            update_data["updated_at"] = datetime.utcnow()
            
            # 更新部门
            await db.departments.update_one(
                {"_id": ObjectId(department_id)},
                {"$set": update_data}
            )
            
            # 返回更新后的部门
            return await DepartmentService.get_department(db, department_id)
            
        except ValueError:
            raise
        except Exception as e:
            logger.error(f"更新部门失败: {e}")
            raise ValueError(f"更新部门失败: {str(e)}")
    
    @staticmethod
    async def delete_department(
        db: AsyncIOMotorDatabase,
        department_id: str
    ) -> bool:
        """
        删除部门
        
        Args:
            db: 数据库连接
            department_id: 部门ID
            
        Returns:
            是否删除成功
        """
        try:
            if not ObjectId.is_valid(department_id):
                raise ValueError("无效的部门ID")
            
            # 检查部门是否存在
            existing = await db.departments.find_one({"_id": ObjectId(department_id)})
            if not existing:
                raise ValueError("部门不存在")
            
            # 检查是否有子部门
            children_count = await db.departments.count_documents({"parent_id": ObjectId(department_id)})
            if children_count > 0:
                raise ValueError(f"该部门下有 {children_count} 个子部门，无法删除。请先删除或移动子部门。")
            
            # 检查是否有用户
            users_count = await db.users.count_documents({"department_id": department_id})
            if users_count > 0:
                raise ValueError(f"该部门下有 {users_count} 个用户，无法删除。请先将用户移动到其他部门。")
            
            # 删除部门
            await db.departments.delete_one({"_id": ObjectId(department_id)})
            
            return True
            
        except ValueError:
            raise
        except Exception as e:
            logger.error(f"删除部门失败: {e}")
            raise ValueError(f"删除部门失败: {str(e)}")
    
    @staticmethod
    async def move_department(
        db: AsyncIOMotorDatabase,
        department_id: str,
        new_parent_id: Optional[str],
        new_order: Optional[int] = None
    ) -> Dict[str, Any]:
        """
        移动部门（调整父部门或排序）
        
        Args:
            db: 数据库连接
            department_id: 部门ID
            new_parent_id: 新的父部门ID（None表示移动到根级）
            new_order: 新的排序值
            
        Returns:
            更新后的部门信息
        """
        try:
            if not ObjectId.is_valid(department_id):
                raise ValueError("无效的部门ID")
            
            # 检查部门是否存在
            existing = await db.departments.find_one({"_id": ObjectId(department_id)})
            if not existing:
                raise ValueError("部门不存在")
            
            update_data = {"updated_at": datetime.utcnow()}
            
            # 更新父部门
            if new_parent_id is not None:
                if new_parent_id == department_id:
                    raise ValueError("不能将自己设为父部门")
                if new_parent_id:
                    if not ObjectId.is_valid(new_parent_id):
                        raise ValueError("无效的父部门ID")
                    parent = await db.departments.find_one({"_id": ObjectId(new_parent_id)})
                    if not parent:
                        raise ValueError("父部门不存在")
                    
                    # 检查循环引用
                    current_parent_id = new_parent_id
                    visited = {department_id}
                    while current_parent_id:
                        if current_parent_id in visited:
                            raise ValueError("不能创建循环的部门层级关系")
                        visited.add(current_parent_id)
                        parent_dept = await db.departments.find_one({"_id": ObjectId(current_parent_id)})
                        if not parent_dept or not parent_dept.get("parent_id"):
                            break
                        current_parent_id = str(parent_dept["parent_id"])
                    
                    update_data["parent_id"] = ObjectId(new_parent_id)
                else:
                    update_data["parent_id"] = None
            else:
                # 保持原有父部门
                pass
            
            # 更新排序
            if new_order is not None:
                update_data["order"] = new_order
            
            # 更新部门
            await db.departments.update_one(
                {"_id": ObjectId(department_id)},
                {"$set": update_data}
            )
            
            # 返回更新后的部门
            return await DepartmentService.get_department(db, department_id)
            
        except ValueError:
            raise
        except Exception as e:
            logger.error(f"移动部门失败: {e}")
            raise ValueError(f"移动部门失败: {str(e)}")

